/**************************************************************************(C)*
 *
 *  This file is part of the GreenHills project
 *  Copyright (C) 2009  Tieto Corporation
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  Not for commmercial use. Only with Tieto permission.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *************************************************************************(C)*/

#pragma once

#include "gpscoordinate.h"

#include <QString>
#include "gpsdll.h"

namespace greenhills {

namespace util {
    class obinstream;
    class ibinstream;
}

namespace gps {

/** @brief Encapsulation of GPS coordinates
 *
 * This class encapsulates GPS coordinates and provides basic operations like
 * assignment, comparison and streaming.
 */

class GH_GPS_CLASS CGPSPosition
{
public:
    /**
     * @brief Creates empty CGPSPosition object
     */
    CGPSPosition();
    /**
     * @brief Creates CGPSPosition object from given longitude and latitude
     * coordinate values.
     *
     * @param pLongitude Absolute terrestial longitude value in float format
     * XXYY.ZZZZ where XX is value in terrestial longitude degrees, YY.ZZZZ is
     * the value in terrestial longitude minutes with decimal part.
     * @param pNorS Specifies whether the given longitude value represents North
     * or South longitude .
     * @param pLatitude Absolute terrestial latitude value in float format
     * XXYY.ZZZZ where XX is value in terrestial latitude degrees, YY.ZZZZ is
     * the value in terrestial latitude minutes with decimal part.
     * @param pEorW Specifies whether the given latitude value represents East
     * or West latitude.
     */
    CGPSPosition(const CGPSCoordinate &pLatitude,
            const CGPSCoordinate &pLongitude);

public:
    /**
     * @brief Returns latitude position
     *
     * @return Latitude position in a float format XXYY.ZZZZ where XX is value
     * in terrestial latitude degrees, YY.ZZZZ is the value in terrestial
     * latitude minutes with decimal part. Positive value means North latitude,
     * negative value means South latitude.
     */
    CGPSLatitude latitude() const {return mLatitude;}

    /**
     * @brief Returns longitude position
     *
     * @return Longitude position in a float format XXYY.ZZZZ where XX is value
     * in terrestial longitude degrees, YY.ZZZZ is the value in terrestial
     * longitude minutes with decimal part. Positive value means East latitude,
     * negative value means West latitude.
     */
    CGPSLongitude longitude() const {return mLongitude;}

    /**
     * @brief Returns GPS position as string
     *
     * @return Longitude position in a string format. The string format is
     * in human readable format AA BB.bbbb {N/S}, CC DD.dddd {E/W}.
     */
    QString toString() const;

    /**
     * @brief Converts latitude value to string
     *
     * @param pLatitude Latitude position. Positive pLatitude value means North
     * latitude, negative value means South latitude.
     * @return Latitude position in human readable format AA BB.bbbb {N/S}.
     */
    static QString latitude2string(float pLatitude);

    /**
     * @brief Converts longitude value to string
     *
     * @param pLongitude Longitude position. Positive pLongitude value means
     * East longitude, negative value means West latitude.
     * @return Longitude position in human readable format AA BB.bbbb {E/W}.
     */
    static QString longitude2string(float pLongitude);

public:
    /**
     * @brief Returns distance to target in meters
     */
    double distanceTo(const CGPSPosition &pTarget) const;
    /**
     * @brief Returns direction to target in degrees
     */
    double directionTo(const CGPSPosition &pTarget) const;

    CGPSPosition positionTo(double pAngle, double pDistance) const;

public:
    /**
     * @brief Assigns GPS position value.
     *
     * @param pPosition A new GPS position value.
     * @return Longitude position in human readable format AA BB.bbbb {E/W}.
     */
    const CGPSPosition& operator =(const CGPSPosition &pPosition);

    /**
     * @brief Compares two GPS positions.
     *
     * @param pPosition A GPS position value to be compared with.
     * @return Returns true if then given GPS positions are the same.
     */
    bool operator == (const CGPSPosition &pPosition);

    /**
     * @brief Stores GPS position to a stream
     *
     * @param pStream A stream object where GPS position value is written to.
     * @return Returns the stream object passed as parameter.
     */
    virtual util::obinstream& operator<<(util::obinstream &pStream) const;

    /**
     * @brief Restores GPS position from a stream
     *
     * @param pStream A stream object where GPS position value is read from.
     * @return Returns the stream object passed as parameter.
     */
    virtual util::ibinstream& operator>>(util::ibinstream &pStream);

private:
    CGPSLatitude mLatitude;
    CGPSLongitude mLongitude;

    static QString float2string(float pCoordinate, char pNegFlg, char pPosFlg);
    static const long EARTH_DIAMETER = 6378000;
};

} //namespace gps
} //namespace greenhills
